const requestBefore = Symbol('requestBefore')
const requestAfter = Symbol('requestAfter')

// 前缀带有_视为私有属性、私有方法

export class WsAxios {
  _config = {
    // 配置
    url: '', // 地址path
    time: 3000, // 心跳间隔(毫秒)--默认为3000毫秒,
    ping: () => {} // ping code
  }

  _ws = null // websocket实例对象
  _requestId = 0 // callbacks对象key
  _callbacks = {} // 回调函数队列
  _lockReconnect = false // 避免重复连接
  _pingTimer = null // 心跳包定时器
  _reconnectTimer = null //  重连定时器

  constructor(config = this._config) {
    Object.assign(this._config, config)
    this._createWebSocket()
  }

  // 拦截器
  intercepts = {
    request: (func) => {
      if (func) {
        WsAxios[requestBefore] = func
      } else {
        WsAxios[requestBefore] = (request) => request
      }
    },
    response: (func) => {
      if (func) {
        WsAxios[requestAfter] = func
      } else {
        WsAxios[requestAfter] = (response) => response
      }
    }
  }

  static [requestBefore](config) {
    return config
  }

  static [requestAfter](response) {
    return response
  }

  _createWebSocket() {
    // 创建websocket实例
    this._ws = new WebSocket(this._config.url)
    this._ws.onopen = () => {
      // 清空定时器
      this.clearAllTimer()
      // 开始心跳
      this._heartBeat()
    }

    this._ws.onmessage = (message) => {
      const data = message.data
      let res = null
      try {
        const parseData = JSON.parse(data)
        res = typeof parseData === 'string' ? JSON.parse(parseData) : parseData
      } catch (error) {
        console.error(`onmessage parse error: message:${data}, ${error.message}`)
      }

      // 根据请求时传给后端的requestId，取出相应的回调函数
      const callback = this._callbacks[res.requestId]
      try {
        callback(res)
      } catch (error) {
        WsAxios[requestAfter](res)
        console.error(`onmessage have some error: ${error.message}, message:${message}`)
      } finally {
        delete this._callbacks[res.requestId]
      }
    }

    this._ws.onclose = () => {
      console.error('onclose：断开连接')
      this._reconnect()
    }

    this._ws.onerror = () => {
      console.error('onerror：断开连接')
      this._reconnect()
    }
  }

  _heartBeat() {
    // 发送心跳包
    this._pingTimer = setTimeout(async () => {
      await this.ping()
      this._heartBeat()
    }, this._config.time)
  }

  _waitForConnection(message) {
    // 等待连接
    setTimeout(() => {
      if (this._ws.readyState === 0) {
        this._waitForConnection(message)
      } else {
        this._ws.send(message)
      }
    }, 1000)
  }

  _reconnect() {
    // 重连
    if (this._lockReconnect) return
    this._lockReconnect = true
    // 没连接上会一直重连，设置延迟避免请求过多
    this._reconnectTimer && clearTimeout(this._reconnectTimer)
    this._reconnectTimer = setTimeout(() => {
      this._lockReconnect = false
      this._createWebSocket()
    }, this._config.time)
  }

  sendCommand(params) {
    // 发送命令
    const requestId = ++this._requestId
    // 本次请求的id,后端响应需要带上本次请求的requestId,用于callbacks回调队列对应的函数
    params.requestId = requestId
    // 请求调调用拦截钩子
    params = { ...params, ...WsAxios[requestBefore](params) }
    const reqMessage = JSON.stringify(params)
    return new Promise((resolve) => {
      this._callbacks.requestId = (res) => {
        // 响应调用拦截钩子
        resolve(WsAxios[requestAfter](res))
      }

      // 添加状态判断，当为OPEN时，发送信息
      const readyState = this._ws.readyState
      if (readyState === 0) {
        // 正在连接
        this._waitForConnection(reqMessage)
      } else if (readyState === 1) {
        // 连接成功，可以通信
        this._ws.send(reqMessage)
      } else if (readyState === 2) {
        // 连接正在关闭
        this._reconnect()
      } else if (readyState === 3) {
        // 连接已经关闭
        this._reconnect()
      }
    })
  }

  clearAllTimer() {
    clearTimeout(this._pingTimer)
    clearTimeout(this._reconnectTimer)
  }

  ping() {
    if (Object.keys(this._callbacks).length === 0) {
      const messageObject = this._config.ping()
      return this.sendCommand(messageObject)
    }
  }

  destroyed () {
    this.clearAllTimer()
    this._ws && this._ws.close()
  }
}
